/*******************************************************************************
 * Copyright (c) 2010 Marcelo Cataldo, Sangeeth Nambiar
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * JungViewer.java
 * 
 * This class performs the necessary actions to display the dependency graph on the 
 * eclipse view. For visualisation, this version of the tool uses the JUNG libraries from 
 * http://jung.sourceforge.net/
 * 
 * Contributors:
 *    Marcelo Cataldo  - initial implementation 
 *    Sangeeth Nambiar - initial implementation
 *******************************************************************************/
package edu.cmu.isr.dinvenio.ui.graphViewer;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.ConstantTransformer;
import org.apache.commons.collections15.functors.MapTransformer;
import org.apache.commons.collections15.map.LazyMap;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Slider;
import org.eclipse.swt.widgets.Text;

import edu.cmu.isr.dinvenio.Activator;
import edu.cmu.isr.dinvenio.model.DepsGraphEdge;
import edu.cmu.isr.dinvenio.model.DepsGraphNode;
import edu.cmu.isr.dinvenio.model.DepsGraphProperty;
import edu.cmu.isr.dinvenio.ui.views.GraphControlsViewPart;
import edu.cmu.isr.dinvenio.ui.views.GraphViewPart;
import edu.uci.ics.jung.algorithms.cluster.EdgeBetweennessClusterer;
import edu.uci.ics.jung.algorithms.layout.AggregateLayout;
import edu.uci.ics.jung.algorithms.layout.CircleLayout;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.ISOMLayout;
import edu.uci.ics.jung.algorithms.layout.KKLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.SpringLayout;
import edu.uci.ics.jung.algorithms.layout.SpringLayout2;
import edu.uci.ics.jung.algorithms.layout.util.Relaxer;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.SparseMultigraph;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.layout.LayoutTransition;
import edu.uci.ics.jung.visualization.util.Animator;

public class JungViewer {
	
	private JungGraph jungGraph;
	
	// Variables used by the viewer
	private VisualizationViewer<DepsGraphNode,DepsGraphEdge> vv;
	private GraphViewPart jungViewPart;
	private GraphControlsViewPart jungControlViewPart;
	
	private Group clusterGroup;
	private Group groupEdgeWidth;
	private Group NodePropertyGroup;
	private Button buttonScramble;
	
	private AggregateLayout<DepsGraphNode,DepsGraphEdge> layout;
	private Hashtable<String,String> availableLayouts;
	
	private boolean groupVerticesBtnState = true;
	private Dimension clusterDimension;
	private int numEdgesToRemove = 0;
	private boolean nodeColorRG;
	
	private final double SIZE_SCALE_MIN = 10;
	private final double SIZE_SCALE_DEFAULT = 10;
	
	public Hashtable<String,java.awt.Color> colorPropertyMap;
	
	public final Color[] similarColors =
	{
		new Color(216, 134, 134),
		new Color(135, 137, 211),
		new Color(134, 206, 189),
		new Color(206, 176, 134),
		new Color(194, 204, 134),
		new Color(145, 214, 134),
		new Color(133, 178, 209),
		new Color(103, 148, 255),
		new Color(60, 220, 220),
		new Color(30, 250, 100)
	};
	
	@SuppressWarnings("unchecked")
	private Map<DepsGraphNode,Paint> vertexPaints = LazyMap.<DepsGraphNode,Paint>decorate(new HashMap<DepsGraphNode,Paint>(),new ConstantTransformer(Color.cyan));
	@SuppressWarnings("unchecked")
	private Map<DepsGraphEdge,Paint> edgePaints = 	LazyMap.<DepsGraphEdge,Paint>decorate(new HashMap<DepsGraphEdge,Paint>(),new ConstantTransformer(Color.blue));
	
	private int edgeWidthScale;
	private int nodeSizeScale;
	private int numSizeScaleBuckets;
	private int numColorScaleBuckets;
	
	// Controls
	private Slider sliderNoEdge;

	
	public JungViewer(JungGraph jG) {
		// Initialise variables
		this.clusterDimension = new Dimension(40,40);
		this.colorPropertyMap        = new Hashtable<String,java.awt.Color>();
		this.nodeColorRG = true;
		this.jungGraph = jG;
		this.jungGraph.setViewer(this);
		setupVisualizationViewer();
		addJungGraphControls();
		
		availableLayouts = new Hashtable<String,String>();
		initialiseAvailableLayouts();
	}

	private void setupVisualizationViewer() {
		jungViewPart = (GraphViewPart) Activator.getGraphView();
		jungControlViewPart = (GraphControlsViewPart) Activator.getGraphControlsView();
		
		//Create a simple layout frame - specify the Fruchterman-Rheingold layout algorithm
		layout = new AggregateLayout<DepsGraphNode,DepsGraphEdge>(new FRLayout<DepsGraphNode,DepsGraphEdge>(this.jungGraph.graph));
		// Create the visualiser
		vv = new VisualizationViewer<DepsGraphNode,DepsGraphEdge>(layout);  
		
		// Set the properties of the viewer
		vv.setPreferredSize(new Dimension(jungViewPart.awtPanel.getSize()));		
		vv.setBackground( Color.white );
		setToolTipTransformers();
		setColorSizeTransformers(); 
		setLabelTransformers();
		
		// Add the viewer to the view
		jungViewPart.awtPanel.add(new GraphZoomScrollPane(vv));
	    
        // Refresh the views
        this.refresh();        
	}
	
	private void addJungGraphControls(){
		addGraphMouseControls();
		addClusterControls();
	    addEdgeWidthScaler();
	    addNodePropertyTools();
	    jungControlViewPart.refresh();
	}
	////////////////////////////////////////////////////////////////////////////////////
	// Functions to add controls/widgets
	////////////////////////////////////////////////////////////////////////////////////
	private void addGraphMouseControls() {
		DefaultModalGraphMouse<DepsGraphNode,DepsGraphEdge> gm = new DefaultModalGraphMouse<DepsGraphNode, DepsGraphEdge>();
		vv.setGraphMouse(gm);
		jungControlViewPart.awtPanel.add(gm.getModeComboBox());
		
		buttonScramble = new Button(jungControlViewPart.parent, SWT.PUSH);
		buttonScramble.setText("Reset Graph");
		buttonScramble.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				Layout<DepsGraphNode, DepsGraphEdge> layout = vv.getGraphLayout();
				layout.initialize();
				Relaxer relaxer = vv.getModel().getRelaxer();
				if(relaxer != null) {
					relaxer.stop();
					relaxer.prerelax();
					relaxer.relax();
				}
			}
		});
	}
	private void setLabelTransformers() {
		vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<DepsGraphNode>());
		vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller<DepsGraphEdge>());		
	}

	////////////////////////////////////////////////////////////////////////////////////
	// Functions related to color and size of edges and vertices
	////////////////////////////////////////////////////////////////////////////////////
	private void setColorSizeTransformers() {
		vv.getRenderContext().setVertexFillPaintTransformer(MapTransformer.<DepsGraphNode,Paint>getInstance(JungViewer.this.vertexPaints));		
		vv.getRenderContext().setEdgeDrawPaintTransformer(MapTransformer.<DepsGraphEdge,Paint>getInstance(JungViewer.this.edgePaints));
		vv.getRenderContext().setEdgeStrokeTransformer(new Transformer<DepsGraphEdge,Stroke>() {
                protected final Stroke THIN = new BasicStroke(1);
                public Stroke transform(DepsGraphEdge e)
                {
                    Paint c = JungViewer.this.edgePaints.get(e);
                    if (c == Color.LIGHT_GRAY)
                        return THIN;
                    else
                		return new BasicStroke(computeEdgeStroke(e));
                }
            });		
	}
	private void addEdgeWidthScaler(){
		groupEdgeWidth = new Group(jungControlViewPart.parent, SWT.BACKGROUND);
		groupEdgeWidth.setLayout(new RowLayout(SWT.HORIZONTAL));
		groupEdgeWidth.setText("Edge width scaler");
		
		Group group = new Group(groupEdgeWidth, SWT.BACKGROUND);
		group.setLayout(new RowLayout(SWT.HORIZONTAL));
		group.setText("Edge width scaler");
		
	    final Slider slider = new Slider(group, SWT.HORIZONTAL);
	    slider.setMaximum(21);
	    slider.setMinimum(1);
	    slider.setIncrement(1);
	    slider.setSelection(1);	  
	    slider.setThumb(1);
	    edgeWidthScale = 1;

	    final Text sliderText = new Text(group, SWT.BACKGROUND);	
	    sliderText.setSize(30,50);
	    sliderText.setText("     " + String.valueOf(slider.getSelection()));
		
	    slider.addListener(SWT.Selection, new Listener() {
	        public void handleEvent(Event event) {
	        	edgeWidthScale = slider.getSelection();
	        	sliderText.setText(String.valueOf(edgeWidthScale));	          
				vv.repaint();
	        }
	      });
	}
	
	private void addNodePropertyTools(){
		NodePropertyGroup = new Group(jungControlViewPart.parent, SWT.BACKGROUND);
		RowLayout l = new RowLayout(SWT.VERTICAL);
		l.fill = true;
		NodePropertyGroup.setLayout(l);
		NodePropertyGroup.setText("Node property Controls:");
		
	    addNodeSizeScaler(NodePropertyGroup);
	    addNodeSizeBucketSlider(NodePropertyGroup);
	    addNodeColorBucketSlider(NodePropertyGroup);
	}
	
	private void addNodeSizeScaler(Group nodePropertyGroup){
		Group group = new Group(nodePropertyGroup, SWT.BACKGROUND);
		group.setLayout(new RowLayout(SWT.HORIZONTAL));
		group.setText("Node size scaler");
		
	    final Slider slider = new Slider(group, SWT.HORIZONTAL);
	    slider.setMaximum(21);
	    slider.setMinimum(1);
	    slider.setIncrement(1);
	    slider.setSelection(1);	  
	    slider.setThumb(1);
	    nodeSizeScale = slider.getSelection();

	    final Text sliderText = new Text(group, SWT.BACKGROUND);	
	    sliderText.setSize(30,50);
	    sliderText.setText("      " + String.valueOf(nodeSizeScale));
		
	    slider.addListener(SWT.Selection, new Listener() {
	        public void handleEvent(Event event) {
	        	nodeSizeScale = slider.getSelection();
	        	sliderText.setText(String.valueOf(nodeSizeScale));	          
				vv.repaint();
	        }
	      });
	}
	private void addNodeSizeBucketSlider(Group nodePropertyGroup){
		// Bucket for node size
		Group group = new Group(nodePropertyGroup, SWT.BACKGROUND);
		group.setLayout(new RowLayout(SWT.HORIZONTAL));
		group.setText("Node size buckets");
		
	    final Slider slider = new Slider(group, SWT.HORIZONTAL);
	    slider.setMaximum(21);
	    slider.setMinimum(5);
	    slider.setIncrement(1);
	    slider.setSelection(10);
	    slider.setThumb(1);
	    numSizeScaleBuckets = slider.getSelection();

	    final Text sliderText = new Text(group, SWT.BACKGROUND);	
	    sliderText.setSize(30,50);
	    sliderText.setText("   " + String.valueOf(numSizeScaleBuckets));
		
	    slider.addListener(SWT.Selection, new Listener() {
	        public void handleEvent(Event event) {
	        	numSizeScaleBuckets = slider.getSelection();
	        	sliderText.setText(String.valueOf(numSizeScaleBuckets));	          
				vv.repaint();
	        }
	      });
	}
	private void addNodeColorBucketSlider(Group nodePropertyGroup){
		// Bucket for node color
		Group group = new Group(nodePropertyGroup, SWT.BACKGROUND);
		group.setLayout(new RowLayout(SWT.HORIZONTAL));
		group.setText("Node color buckets");
		
	    final Slider slider = new Slider(group, SWT.HORIZONTAL);
	    slider.setMaximum(20);
	    slider.setMinimum(1);
	    slider.setIncrement(1);
	    slider.setSelection(10);
	    slider.setThumb(1);	  
	    numColorScaleBuckets = slider.getSelection();

	    final Text sliderText = new Text(group, SWT.BACKGROUND);	
	    sliderText.setSize(30,50);
	    sliderText.setText("   " +String.valueOf(numColorScaleBuckets));
		
	    slider.addListener(SWT.Selection, new Listener() {
	        public void handleEvent(Event event) {
	        	numColorScaleBuckets = slider.getSelection();
	        	sliderText.setText(String.valueOf(numColorScaleBuckets));	          
	        	updateNodeColors();
	        	vv.repaint();
	        }
	      });
	}
	private void updateNodeColors() {	
		String p = jungGraph.getPropertyForNodeColor();
				
		Collection<DepsGraphNode> Nodes = jungGraph.jungGraphNodes.values();
		for (DepsGraphNode n : Nodes){		
			if (p != null ) {
				String pv = n.getPropertyValue(p);	
				if (pv != null) {
					if(nodeColorRG){
						double val = Double.parseDouble(pv);
						vertexPaints.put(n, scaleNodePropertyToRGColor(val));
					}
					else {
						vertexPaints.put(n, jungGraph.getColorFromTable(pv));
					}
				}
				else {
					// When the property is not available for a particular node, then the color shall be white !
					vertexPaints.put(n, Color.white);
				}
			}
			else {
				// When the property is not available for a particular node, then the color shall be white !
				vertexPaints.put(n, Color.white);
			}
		}
	}
	private void updateNodeSize(){
		if (jungGraph.getPropertyForNodeSize() != null){
			vv.getRenderContext().setVertexShapeTransformer(new Transformer<DepsGraphNode,Shape>() {
				@Override
				public Shape transform(DepsGraphNode n) {
					String pv = n.getPropertyValue(jungGraph.getPropertyForNodeSize());
					int size;
					if(pv != null){
						double val = Double.parseDouble(pv);
						size = scaleValueToNodeSize(val);
					}
					else{
						size = (int)SIZE_SCALE_DEFAULT * nodeSizeScale;	
					}										
					float radii = size;
					Shape circle = new Ellipse2D.Float(-radii,-radii, radii*2, radii*2);
					return circle;					
				}});
		}
		else
		{
			vv.getRenderContext().setVertexShapeTransformer(new Transformer<DepsGraphNode,Shape>() {
				@Override
				public Shape transform(DepsGraphNode n) {
					int size = (int)SIZE_SCALE_DEFAULT * nodeSizeScale;					
					float radii = size;
					//System.out.println(" Default radius :" + radii);
					Shape circle = new Ellipse2D.Float(-radii,-radii, radii*2, radii*2);
					return circle;					
				}});		
		}
	}
	public void refreshNodes() {
		updateNodeColors();
		updateNodeSize();
		vv.repaint();
	}

	private void setToolTipTransformers(){
		vv.setVertexToolTipTransformer(new Transformer<DepsGraphNode,String>() {
			@Override
			public String transform(DepsGraphNode n) {
				return n.getToolTip();
			}});
		vv.setEdgeToolTipTransformer(new Transformer<DepsGraphEdge,String>() {
			@Override
			public String transform(DepsGraphEdge e) {
				return e.getToolTip();
			}});
	}
	////////////////////////////////////////////////////////////////////////////////////
	// Functions related to grouping and clustering
	////////////////////////////////////////////////////////////////////////////////////
	private void addClusterControls() {
		Group group;
		
		clusterGroup = new Group(jungControlViewPart.parent, SWT.BACKGROUND);
		RowLayout l = new RowLayout(SWT.VERTICAL);
		l.fill = true;
		clusterGroup.setLayout(l);
		clusterGroup.setText("Grouping Controls:");
		
		////////////////////////////////////////////////////////////////
		// Clustering Group
		//////////////////////////////////////////////////////////////
		group = new Group(clusterGroup, SWT.BACKGROUND);
		group.setLayout(new RowLayout(SWT.VERTICAL));
		group.setText("Options");
		
		// Selection to group or un-group
		final Button groupVerticesBtn = new Button(group, SWT.CHECK);
		groupVerticesBtn.setText("Group Clusters");
		groupVerticesBtn.setSelection(true);
		groupVerticesBtnState = true;
		groupVerticesBtn.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {				
				groupVerticesBtnState = groupVerticesBtn.getSelection();
			}
		});
		
		////////////////////////////////////////////////////////////////
		// Add the slider for selecting the number of edges to remove
		//////////////////////////////////////////////////////////////
		group = new Group(clusterGroup, SWT.BACKGROUND);		
		RowLayout rl = new RowLayout(SWT.HORIZONTAL);
		rl.justify = true;
		group.setLayout(rl);
		group.setText("Edges removed for clustering:");
	    
		final Button buttonMinus = new Button(group, SWT.PUSH);
		buttonMinus.setText("-");
		
	    final Text sliderText2 = new Text(group, SWT.FOREGROUND);	
	    sliderText2.setEditable(false);
	    sliderText2.setText("     ");
	    sliderText2.setText(" " + String.valueOf(numEdgesToRemove)+ " ");
	    
		final Button buttonPlus = new Button(group, SWT.PUSH);
		buttonPlus.setText("+");
	    
		buttonMinus.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
	        	numEdgesToRemove--;
	        	if (numEdgesToRemove < 0){numEdgesToRemove = 0;}
	        	sliderText2.setText(String.valueOf(numEdgesToRemove));          	        	
	        	clusterAndRecolor(layout, numEdgesToRemove, similarColors,groupVerticesBtnState);
				vv.repaint();
			}
		});
		
		buttonPlus.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
	        	numEdgesToRemove++;
	        	if (numEdgesToRemove > jungGraph.getNumEdges()){numEdgesToRemove = jungGraph.getNumEdges();}
	        	sliderText2.setText(String.valueOf(numEdgesToRemove));          	        	
	        	clusterAndRecolor(layout, numEdgesToRemove, similarColors,groupVerticesBtnState);
				vv.repaint();
			}
		});
		
		////////////////////////////////////////////////////////////////
		// Create slider to adjust the cluster size
		//////////////////////////////////////////////////////////////
		group = new Group(clusterGroup, SWT.BACKGROUND);
		group.setLayout(new RowLayout(SWT.HORIZONTAL));
		group.setText("Cluster Size:");
		
	    final Slider sliderClusterSize = new Slider(group, SWT.HORIZONTAL);
	    sliderClusterSize.setMaximum(201);
	    sliderClusterSize.setMinimum(40);
	    sliderClusterSize.setIncrement(5);
	    sliderClusterSize.setPageIncrement(5);
	    sliderClusterSize.setSelection(40);	  
	    sliderClusterSize.setThumb(1);

	    final Text sliderClusterSizeText2 = new Text(group, SWT.BACKGROUND);	
	    sliderClusterSizeText2.setText("     " + String.valueOf(sliderClusterSize.getSelection()));
		
	    sliderClusterSize.addListener(SWT.Selection, new Listener() {
	    	@Override
	    	public void handleEvent(Event event) {
	        	int perspectiveValue =sliderClusterSize.getSelection();
	        	sliderClusterSizeText2.setText(String.valueOf(perspectiveValue));	          
	        	JungViewer.this.clusterDimension.height = perspectiveValue;
	        	JungViewer.this.clusterDimension.width = perspectiveValue;
	        	clusterAndRecolor(layout, numEdgesToRemove, similarColors,groupVerticesBtnState);
				vv.repaint();
	        }
	      });
	}
	public void clusterAndRecolor(AggregateLayout<DepsGraphNode,DepsGraphEdge> layout,int numEdgesToRemove, Color[] colors, boolean groupClusters) {
		Graph<DepsGraphNode,DepsGraphEdge> g = layout.getGraph();
        layout.removeAll();

		EdgeBetweennessClusterer<DepsGraphNode,DepsGraphEdge> clusterer = new EdgeBetweennessClusterer<DepsGraphNode,DepsGraphEdge>(numEdgesToRemove);
		Set<Set<DepsGraphNode>> clusterSet = clusterer.transform(g);
		List<DepsGraphEdge> edges = clusterer.getEdgesRemoved();

		int i = 0;
		//Set the colors of each node so that each cluster's vertices have the same color
		for (Iterator<Set<DepsGraphNode>> cIt = clusterSet.iterator(); cIt.hasNext();) {
			Set<DepsGraphNode> vertices = cIt.next();
			Color c = colors[i % colors.length];
			colorCluster(vertices, c);
			if(groupClusters == true) {
				groupCluster(layout, vertices);
			}
			i++;
		}

		for (DepsGraphEdge e : g.getEdges()) {
			if (edges.contains(e)) {
				edgePaints.put(e, Color.lightGray);
			} else {
				putEdgeColorInMap(e);
			}
		}
	}
	private void colorCluster(Set<DepsGraphNode> vertices, Color c) {
		for (DepsGraphNode v : vertices) {
			vertexPaints.put(v, c);
		}
	}
	private void groupCluster(AggregateLayout<DepsGraphNode,DepsGraphEdge> layout, Set<DepsGraphNode> vertices) {
		if(vertices.size() < layout.getGraph().getVertexCount()) {
			Point2D center = layout.transform(vertices.iterator().next());
			Graph<DepsGraphNode,DepsGraphEdge> subGraph = SparseMultigraph.<DepsGraphNode,DepsGraphEdge>getFactory().create();
			for(DepsGraphNode v : vertices) {
				subGraph.addVertex(v);
			}
			Layout<DepsGraphNode,DepsGraphEdge> subLayout = new CircleLayout<DepsGraphNode,DepsGraphEdge>(subGraph);
			subLayout.setInitializer(vv.getGraphLayout());
			subLayout.setSize(this.clusterDimension);

			layout.put(subLayout,center);
			vv.repaint();
		}
	}
	public void putEdgeColorInMap(DepsGraphEdge e){
		if (e.layer.getVisibility()) {
			edgePaints.put(e, e.getColor());			
		} else {
			edgePaints.put(e, Color.lightGray);
		}	
	}
	public int computeEdgeStroke(DepsGraphEdge e)
	{
		// Unlike the scales for node colour/size, here we use a logarithmic scale
		int c = e.layer.edgeWeight_min;
		int s = 0;
		while(c < e.getEdgeWeight()) { s++; c *= 5; }
		if (s == 0) { s = 1; }
		return s * edgeWidthScale;
	}
	private Paint scaleNodePropertyToRGColor(double v) {
		int s = (int)Math.ceil((jungGraph.getNodeColorMax() - jungGraph.getNodeColorMin()) / ((double)numColorScaleBuckets));
		v -= jungGraph.getNodeColorMin();
		if (s > 0) { s = ((int)v) / s; }		
		int colbucket = 255/numColorScaleBuckets;
		int r = colbucket * s;
		int g = 255 - (r);
		return new Color(r, g, 0);
	}
	private int scaleValueToNodeSize(double v) 
	{
		final double SIZE_SCALE_MAX = 60;
		
		int s = (int)Math.ceil((jungGraph.getNodeSizeMax() - jungGraph.getNodeSizeMin()) / ((double)numSizeScaleBuckets));
		int r = (int)Math.ceil((SIZE_SCALE_MAX - SIZE_SCALE_MIN) / ((double)numSizeScaleBuckets));
		v -= jungGraph.getNodeSizeMin();
		if (s > 0) { s = ((int)v) / s; }
		return (int) ( (SIZE_SCALE_MIN + (r * s)) * nodeSizeScale);
	}
	private void updateViewerPropertiesOnGraphChanges(){
		sliderNoEdge.setMaximum(this.jungGraph.graph.getEdgeCount()+1);
	}
	public void drawGraph (){
		jungGraph.transtlateDepsGraphToJung();
		updateViewerPropertiesOnGraphChanges();
	}
	public void clearGraph(){		
		jungGraph.clearGraph();
		vv.repaint();
		jungViewPart.refresh();
	}

	public void refresh() {
		vv.repaint();
		jungViewPart.refresh();
	}

	public void setNodeColorStyleRG(boolean b) {
		nodeColorRG = b;		
	}

	public void refreshGraphSize() {
		vv.setPreferredSize(new Dimension(jungViewPart.awtPanel.getSize()));
	}
	private void initialiseAvailableLayouts() {
		availableLayouts.put("KK Layout",         	"edu.uci.ics.jung.algorithms.layout.KKLayout");
		availableLayouts.put("FR Layout",         	"edu.uci.ics.jung.algorithms.layout.FRLayout");
		availableLayouts.put("FR Layout 2",     	"edu.uci.ics.jung.algorithms.layout.FRLayout2");
		availableLayouts.put("Circle Layout",   	"edu.uci.ics.jung.algorithms.layout.CircleLayout");
		availableLayouts.put("Spring Layout",   	"edu.uci.ics.jung.algorithms.layout.SpringLayout");
		availableLayouts.put("Spring Layout 2", 	"edu.uci.ics.jung.algorithms.layout.SpringLayout2");
		availableLayouts.put("ISOM Layout",     	"edu.uci.ics.jung.algorithms.layout.ISOMLayout");			
//		availableLayouts.put("Abstract Layout", 	"edu.uci.ics.jung.algorithms.layout.AbstractLayout");
//		availableLayouts.put("Aggregate Layout",	"edu.uci.ics.jung.algorithms.layout.AggregateLayout");
//		availableLayouts.put("Balloon Layout",  	"edu.uci.ics.jung.algorithms.layout.BalloonLayout");
//		availableLayouts.put("Radial Tree Layout",	"edu.uci.ics.jung.algorithms.layout.RadialTreeLayout");
//		availableLayouts.put("Tree Layout",     	"edu.uci.ics.jung.algorithms.layout.TreeLayout");

		
	}

	public Enumeration<String> getAvailableLayouts() {
		return availableLayouts.keys();
	}

	public String getDefaultLayout() {
		return "FR Layout";
	}

	public void setLayout(String layout) {
		if(layout != null){
			
			Layout<DepsGraphNode, DepsGraphEdge> l;
			try {
				Class layoutClass = Class.forName(availableLayouts.get(layout));
				Constructor con = layoutClass.getConstructor(Graph.class);
				Object obj = con.newInstance(this.jungGraph.graph);
				
				l = (Layout<DepsGraphNode, DepsGraphEdge>)obj;
	            l.setInitializer(vv.getGraphLayout());
	            l.setSize(vv.getSize());
	            
				LayoutTransition<DepsGraphNode,DepsGraphEdge> lt = new LayoutTransition<DepsGraphNode,DepsGraphEdge>(vv, vv.getGraphLayout(), l);
				Animator animator = new Animator(lt);
				animator.start();
				vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
				vv.repaint();
				
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				System.err.println( layout + " The class must be in class path.");
				e.printStackTrace();
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public void disposeWidgets() {
		jungViewPart.disposeWidgets();
		jungControlViewPart.disposeWidgets();
		clusterGroup.dispose();
		groupEdgeWidth.dispose();
		NodePropertyGroup.dispose();
		buttonScramble.dispose();
	}
}
